﻿using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Ocsp;
using Org.BouncyCastle.Security;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace LicenseTools
{
    public class LicenseServer
    {
       /// <summary>
       /// 生成公钥和私钥（xml）
       /// </summary>
        public static string[] GenXmlKey()
        {
            try
            {
                using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
                {
                    string publicKey = rsa.ToXmlString(false); // 公钥
                    string privateKey = rsa.ToXmlString(true); // 私钥

                    return new string[] { publicKey, privateKey };
                }
            }
            catch (Exception)
            {
                return null;
            }
          
        }



        /// <summary>
        /// 根据机器序列号生成授权License 
        /// </summary>
        /// <param name="SerialNo"></param>
        /// <param name="privateKey"></param>
        /// <param name="startTime"></param>
        /// <param name="endTime"></param>
        /// <returns></returns>
        public static string GenLicense(string SerialNo,string privateKey,long startTime,long endTime)
        {
            try
            {
                //生成序列号签名
                byte[] sign = Sign(SerialNo, privateKey);

                //签名
                byte[] sn = Convert.FromBase64String(SerialNo);

                //AES加密授权时间
                byte[] aesKey = sn.Skip(0).Take(32).ToArray();
                byte[] aesIV = sn.Skip(32).Take(16).ToArray();
                byte[] time = EncryptStringToBytes(startTime.ToString() + "|" + endTime.ToString() + "|" + startTime.ToString(), aesKey, aesIV);

                //序列号 + 序列号签名 + 授权时间
                byte[] lic = new byte[sn.Length + sign.Length + time.Length];
                Buffer.BlockCopy(sn, 0, lic, 0, sn.Length);
                Buffer.BlockCopy(sign, 0, lic, sn.Length, sign.Length);
                Buffer.BlockCopy(time, 0, lic, sn.Length + sign.Length, time.Length);

                //生成License
                string license = string.Empty;
                license = Convert.ToBase64String(lic);
                return license;
            }
            catch (Exception)
            {
                return null;
            }
          
        }




        /// <summary>
        /// 获取license的详细信息，包括机器序列号和授权时间
        /// </summary>
        /// <param name="license"></param>
        /// <returns></returns>
        public static string[] GetLicenseDetail(string license)
        {
            try
            {
                byte[] lic = Convert.FromBase64String(license);

                //获取加密数据长度
                int dataCnt = BitConverter.ToInt16(lic, 48);

                //获取机器码
                byte[] sn = lic.Skip(0).Take(50 + dataCnt).ToArray();

                //获取时间
                byte[] aesKey = sn.Skip(0).Take(32).ToArray();
                byte[] aesIV = sn.Skip(32).Take(16).ToArray();
                byte[] times = lic.Skip(48 + 2 + dataCnt + 128).ToArray();
                string[] timeStr = DecryptStringFromBytes(times, aesKey, aesIV).Split('|');


                //依次返回机器码和三个时间
                return new string[]
                {
                 Convert.ToBase64String(sn),
                 timeStr[0],
                 timeStr[1],
                 timeStr[2]
                };
            }
            catch (Exception)
            {
                return null;
            }
        }


        #region private methods

            /// <summary>
            /// 最大加密长度
            /// </summary>
        private const int MAX_ENCRYPT_BLOCK = 245;


        /// <summary>
        /// 用私钥进行RSA加密
        /// </summary>
        /// <param name="xmlPrivateKey"> 私钥(XML格式字符串)</param>
        /// <param name="content">要加密的数据</param>
        /// <returns> 加密后的数据 </returns>
        private static string Encrypt(string xmlPrivateKey, string content)
        {
            //加载私钥
            RSACryptoServiceProvider privateRsa = new RSACryptoServiceProvider();
            privateRsa.FromXmlString(xmlPrivateKey);

            //转换密钥
            AsymmetricCipherKeyPair keyPair = DotNetUtilities.GetKeyPair(privateRsa);
            IBufferedCipher c = CipherUtilities.GetCipher("RSA/ECB/PKCS1Padding"); //使用RSA/ECB/PKCS1Padding格式

            c.Init(true, keyPair.Private);//第一个参数为true表示加密，为false表示解密；第二个参数表示密钥
            byte[] dataToEncrypt = Encoding.UTF8.GetBytes(content);//获取字节

            byte[] cache;
            int time = 0;//次数
            int inputLen = dataToEncrypt.Length;
            int offSet = 0;

            MemoryStream outStream = new MemoryStream();
            while (inputLen - offSet > 0)
            {
                if (inputLen - offSet > MAX_ENCRYPT_BLOCK)
                {
                    cache = c.DoFinal(dataToEncrypt, offSet, MAX_ENCRYPT_BLOCK);
                }
                else
                {
                    cache = c.DoFinal(dataToEncrypt, offSet, inputLen - offSet);
                }
                //写入
                outStream.Write(cache, 0, cache.Length);

                time++;
                offSet = time * MAX_ENCRYPT_BLOCK;
            }

            byte[] resData = outStream.ToArray();

            string strBase64 = Convert.ToBase64String(resData);
            outStream.Close();
            return strBase64;
        }


        /// <summary>
        /// 签名
        /// </summary>
        /// <param name="str">需签名的数据</param>
        /// <returns>签名后的值</returns>
        private static byte[] Sign(string str, string privateKey)
        {
            //根据需要加签时的哈希算法转化成对应的hash字符节
            byte[] bt = Convert.FromBase64String(str);
            byte[] rgbHash = null;

            //SHA256
            var csp = new SHA256CryptoServiceProvider();
            rgbHash = csp.ComputeHash(bt);

            RSACryptoServiceProvider key = new RSACryptoServiceProvider();
            key.FromXmlString(privateKey);
            RSAPKCS1SignatureFormatter formatter = new RSAPKCS1SignatureFormatter(key);
            //此处是你需要加签的hash算法，需要和上边你计算的hash值的算法一致，不然会报错。
            formatter.SetHashAlgorithm("SHA256");
            byte[] inArray = formatter.CreateSignature(rgbHash);

            return inArray;
        }


        /// <summary>
        /// AES加密
        /// </summary>
        /// <param name="plainText"></param>
        /// <param name="Key"></param>
        /// <param name="IV"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        static byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
        {
            // Check arguments.
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("IV");
            byte[] encrypted;

            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV;

                // Create an encryptor to perform the stream transform.
                ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for encryption.
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {
                            //Write all data to the stream.
                            swEncrypt.Write(plainText);
                        }
                        encrypted = msEncrypt.ToArray();
                    }
                }
            }

            // Return the encrypted bytes from the memory stream.
            return encrypted;
        }




        /// <summary>
        /// AES解密
        /// </summary>
        /// <param name="cipherText"></param>
        /// <param name="Key"></param>
        /// <param name="IV"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        static string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
        {
            // Check arguments.
            if (cipherText == null || cipherText.Length <= 0)
                throw new ArgumentNullException("cipherText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("IV");

            // Declare the string used to hold
            // the decrypted text.
            string plaintext = null;

            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV;

                // Create a decryptor to perform the stream transform.
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for decryption.
                using (MemoryStream msDecrypt = new MemoryStream(cipherText))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {

                            // Read the decrypted bytes from the decrypting stream
                            // and place them in a string.
                            plaintext = srDecrypt.ReadToEnd();
                        }
                    }
                }
            }

            return plaintext;
        }

        #endregion
    }

}
